﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.UI;

namespace _5E.Utils.UserControlHelper
{
    /// <summary>
    /// 用户控件渲染时处理
    /// </summary>
    public class UserControlRenderingHandler : IHttpHandler
    {
        #region 变量

        private static Dictionary<Type, Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>> s_MetadataCache =
            new Dictionary<Type, Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>>();

        private static Dictionary<PropertyInfo, object> s_DefaultValue = new Dictionary<PropertyInfo, object>();

        private static object s_Mutex = new object();

        #endregion

        #region 实例方法

        public void ProcessRequest(HttpContext context)
        {
            string appRelativePath = context.Request.AppRelativeCurrentExecutionFilePath;
            string controlPath = appRelativePath.Replace(".uc", ".ascx");

            var view = new ViewManager<UserControl>();
            var recordControl = view.LoadViewControl(controlPath);

            SetUserControlPropertyValues(recordControl, context);

            context.Response.ContentType = "text/html";
            context.Response.Write(view.RenderView(recordControl));
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        #endregion

        #region 静态方法

        private static void SetUserControlPropertyValues(UserControl control, HttpContext context)
        {
            var type = control.GetType();
            var metadata = GetMetadata(type);
            foreach (var property in metadata.Keys)
            {
                object value = GetValue(metadata[property], context) ?? GetDefaultValue(property);
                if (value != null)
                {
                    property.SetValue(control, Convert.ChangeType(value, property.PropertyType), null);
                }
            }
        }

        private static Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> GetMetadata(Type type)
        {
            Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> metadata;
            if (!s_MetadataCache.TryGetValue(type, out metadata))
            {
                lock (s_Mutex)
                {
                    if (!s_MetadataCache.TryGetValue(type, out metadata))
                    {
                        metadata = LoadMetadata(type);
                    }
                }
            }
            return metadata;
        }

        private static Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> LoadMetadata(Type type)
        {
            var metadata = new Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>();
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
            foreach (var property in properties)
            {
                var attributes = property.GetCustomAttributes(typeof(UserControlRenderingPropertyAttribute), true);
                if (0 < attributes.Length)
                {
                    metadata.Add(property, new List<UserControlRenderingPropertyAttribute>(
                        attributes.Cast<UserControlRenderingPropertyAttribute>()));
                }
            }
            return metadata;
        }

        private static object GetValue(IEnumerable<UserControlRenderingPropertyAttribute> metadata, HttpContext context)
        {
            foreach (var attribute in metadata)
            {
                var collection = context.Request.Params;
                object value = collection[attribute.Key];
                if (null != value)
                {
                    return value;
                }
            }
            return null;
        }

        private static object GetDefaultValue(PropertyInfo property)
        {
            object value;
            if (!s_DefaultValue.TryGetValue(property, out value))
            {
                lock (s_Mutex)
                {
                    if (!s_DefaultValue.TryGetValue(property, out value))
                    {
                        var attributes = property.GetCustomAttributes(typeof(DefaultValueAttribute), true);
                        value = 0 < attributes.Length ? (attributes[0] as DefaultValueAttribute).Value : null;
                        s_DefaultValue.Add(property, value);
                    }
                }
            }
            return value;
        }

        #endregion
    }
}
